package loan.calc.calculator.impl;

import loan.calc.constant.dict.FinalPeriodMergeTypeEnum;
import loan.calc.constant.dict.RepayFrequencyTypeEnum;
import loan.calc.dto.LoanDTO;
import loan.calc.dto.LoanRepaySegmentDTO;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.time.Month.*;

/**
 * @author v_ECD963
 */
@Component("repaySegmentNextRepayDateCalculator")
public class LoanRepaymentSegmentNextRepayDateCalculator extends AbstractCalculator {

    private static final Integer SORT_CODE = 300;

    /**
     * 固定季的月份
     */
    private static final List<Integer> FIXED_MONTH = Stream.of(MARCH, JUNE, SEPTEMBER, DECEMBER)
            .map(Month::getValue)
            .collect(Collectors.toList());

    @Override
    public Integer calculatorSort() {
        return SORT_CODE;
    }

    @Override
    public String calculatorName() {
        return "还款区段下次还款日填充";
    }

    @Override
    public void calculate(LoanDTO loan) {
        loan.getLoanRepaySegmentList().forEach(segment -> {
            if (segment.getSegmentStartDate() == null) {
                segment.setSegmentStartDate(loan.getStartDate());
            }
            if (segment.getSegmentMaturityDate() == null) {
                segment.setSegmentMaturityDate(loan.getMaturityDate());
            }
            if (segment.getLastRepaymentDate() == null) {
                segment.setLastRepaymentDate(segment.getSegmentStartDate());
            }
            LocalDate nextRepaymentDate = doCalculate(segment);
            printRepaymentSegmentDebugLog(segment, "下次还款日:" + nextRepaymentDate);
            segment.setNextRepaymentDate(nextRepaymentDate);
        });
    }

    public LocalDate doCalculate(LoanRepaySegmentDTO segment) {
        LocalDate segmentStartDate = segment.getSegmentStartDate();
        LocalDate segmentMaturityDate = segment.getSegmentMaturityDate();
        LocalDate lastRepaymentDate = segment.getLastRepaymentDate();
        RepayFrequencyTypeEnum repayFrequencyType = segment.getRepayFrequencyType();
        ChronoUnit frequencyUnit = segment.getRepayFrequencyUnit();
        Integer repayFrequency = segment.getRepayFrequency();

        //如果上次还款日为区段结束日期，则下次还款日为null
            /*if (lastRepaymentDate.equals(segmentStartDate)) {
                return null;
            }*/

        //如果是一次性还本付息，那么下次还款日就是区段结束日
        if (RepayFrequencyTypeEnum.ONCE == repayFrequencyType) {
            return segmentMaturityDate;
        }
        //默认还款日
        Integer defaultRepaymentDay = segment.getDefaultRepayDay();

        //下次还款日初始化为上次还款日
        LocalDate nextRepaymentDate = lastRepaymentDate;

        //固定季的情况下，计算下次还款日
        if (RepayFrequencyTypeEnum.FIXED_QUARTER == repayFrequencyType) {
            do {
                nextRepaymentDate = nextRepaymentDate.plus(1, ChronoUnit.MONTHS).withDayOfMonth(defaultRepaymentDay);
            } while (!FIXED_MONTH.contains(nextRepaymentDate.getMonthValue()));
        } else {
            //非固定季的情况，计算下次还款日
            if (ChronoUnit.DAYS == frequencyUnit) {
                //周期单位为天的情况，则下次还款日就是上次还款日+周期值，不考虑默认还款日
                nextRepaymentDate = nextRepaymentDate.plus(repayFrequency, frequencyUnit);
            } else {
                //周期单位大于等于月的情况，需要考虑默认还款日
                nextRepaymentDate = lastRepaymentDate.plus(repayFrequency, frequencyUnit).withDayOfMonth(defaultRepaymentDay);
            }
        }
        //如果首次还款日与放款日之间的期限小于1个还款周期，那么该期次不还款，推迟到下一个周期
        if (lastRepaymentDate.isEqual(segmentStartDate) && nextRepaymentDate.minus(repayFrequency, frequencyUnit).isBefore(lastRepaymentDate)) {
            nextRepaymentDate = nextRepaymentDate.plus(repayFrequency, frequencyUnit).withDayOfMonth(defaultRepaymentDay);
        }
        //如果下次还款日与区段结束日期在同一个年月，并且末次还款合并，则末次还款日不使用默认还款日而使用区段到期日
        if (FinalPeriodMergeTypeEnum.MERGE.equals(segment.getFinalPeriodMergeType())
                && yearMonthEqual(nextRepaymentDate, segmentMaturityDate)
                && nextRepaymentDate.plus(repayFrequency, frequencyUnit).isAfter(segmentMaturityDate)) {
            nextRepaymentDate = segmentMaturityDate;
        }
        //如果计算所得还款日大于区段到期日，那么取区段到期日为下一还款日
        if (nextRepaymentDate.isAfter(segmentMaturityDate)) {
            nextRepaymentDate = segmentMaturityDate;
        }
        return nextRepaymentDate;

    }

    /**
     * 判断两个日期是否在同一个年月
     *
     * @param someDate  日期
     * @param otherDate 日期
     * @return 是否
     */
    private boolean yearMonthEqual(LocalDate someDate, LocalDate otherDate) {
        return someDate.getYear() == otherDate.getYear() && someDate.getMonthValue() == otherDate.getMonthValue();
    }
}
